﻿// <copyright file="main.js" company="CR Coderz">
// Copyright (c) 2013 All Right Reserved, http://crcoderz.com/
//
// This source is subject to the Microsoft Permissive License.
// Please see the License.txt file for more information.
// All other rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// </copyright>
// <author>Sabry Moulana</author>
// <email>sabry.moulana@gmail.com</email>
// <date>2013-07-22</date>
// <summary>The main js class for the three.js based implementation of the HTML5 mobile app of the BuildIT Framework</summary>

// MAIN
// standard global variables
var container, scene, camera, renderer, controls, stats, projector;
var objects = [];
var keyboard = new THREEx.KeyboardState();
var clock = new THREE.Clock();
var width = window.innerWidth;
var height = window.innerHeight;
var colorChangeClick = false;
// custom global variables
var sphere, sphere1;
var parameters;
var gui;
var currentObject = { selected: null, selectedMesh: null };
var loader;
var directionVector = new THREE.Vector3();
var ray = new THREE.ReusableRay();
var SCREEN_HEIGHT = 150;//window.innerHeight;
var SCREEN_WIDTH = 300;//window.innerWidth;
var floor;
var clickInfo = {
    x: 0,
    y: 0,
    userHasClicked: false
};

init();
animate();

// FUNCTIONS
function init() {
    // KEYDOWN EVENT LISTENER
    window.addEventListener("keydown", doKeyDown)

    // SCENE
    scene = new THREE.Scene();

    // CAMERA
    var SCREEN_WIDTH = window.innerWidth, SCREEN_HEIGHT = window.innerHeight;
    var VIEW_ANGLE = 45, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000;
    camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
    scene.add(camera);
    camera.position.set(0, 150, 400);
    camera.lookAt(scene.position);

    // INITIATES THE PROJECTOR CLASS THAT IS USED TO CALCULATE THE INTERSECTIONS
    projector = new THREE.Projector();

    // RENDERER
    if (Detector.webgl)
        renderer = new THREE.WebGLRenderer({ antialias: true });
    else
        renderer = new THREE.CanvasRenderer();
    renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
    container = document.getElementById('ThreeJS');
    container.appendChild(renderer.domElement);

    // WINDOW EVENTS
    THREEx.WindowResize(renderer, camera);
    THREEx.FullScreen.bindKey({ charCode: 'm'.charCodeAt(0) });

    // CONTROLS
    controls = new THREE.OrbitControls(camera, renderer.domElement);

    // STATS
    stats = new Stats();
    stats.domElement.style.position = 'absolute';
    stats.domElement.style.bottom = '0px';
    stats.domElement.style.zIndex = 100;
    container.appendChild(stats.domElement);

    // LIGHT
    var light = new THREE.PointLight(0xffffff);
    light.position.set(-100, 150, 100);

    // need to add an ambient light
    // for ambient colors to be visible
    // make the ambient light darker so that
    // it doesn't overwhelm (like emmisive light)
    var light2 = new THREE.AmbientLight(0x404040);
    light2.position.set(light.position);
    scene.add(light2);
    var lightbulbGeometry = new THREE.SphereGeometry(10, 16, 8);
    var lightbulbMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff, transparent: true, opacity: 0.8, blending: THREE.AdditiveBlending });
    var wireMaterial = new THREE.MeshBasicMaterial({ color: 0x000000, wireframe: false });
    var materialArray = [lightbulbMaterial, wireMaterial];
    var lightbulb = THREE.SceneUtils.createMultiMaterialObject(lightbulbGeometry, materialArray);
    lightbulb.position = light.position;
    //scene.add(lightbulb);
    scene.add(light);

    // FLOOR
    //var floorTexture = new THREE.ImageUtils.loadTexture('images/wood.jpg');
    //floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping;
    //floorTexture.repeat.set(10, 10);
    //var floorMaterial = new THREE.MeshBasicMaterial({ map: floorTexture, side: THREE.DoubleSide });
    //var floorGeometry = new THREE.PlaneGeometry(1000, 1000, 10, 10);
    //floor = new THREE.Mesh(floorGeometry, floorMaterial);
    //floor.position.y = -100;
    //floor.rotation.x = Math.PI / 2;
    //scene.add(floor);

    // SKYBOX/FOG
    //var skyBoxGeometry = new THREE.SphereGeometry( 10000, 10000, 10000 );
    //var skyBoxMaterial = new THREE.MeshBasicMaterial( { color: 0x9999ff, side: THREE.BackSide } );
    //var skyBox = new THREE.Mesh( skyBoxGeometry, skyBoxMaterial );
    // scene.add(skyBox);
    //  scene.fog = new THREE.FogExp2(0x9999ff, 0.00025);

    ////////////
    // CUSTOM //
    ////////////
    //var newMan = new Shape(SHAPES.CUSTOM, 'object/male/male02.obj', 'object/male/male02_dds.mtl');
    //newMan.drawShape(10, 10, -200, -80, 0);

    //var newWoman = new Shape(SHAPES.CUSTOM, 'object/female/female02.obj', 'object/female/female02.mtl');
    //newWoman.drawShape(10, 10, 200, -80, 0);

    loadCollada('models/Goetz Sofa from Smart Furniture.dae');

    var sphereGeometry = new THREE.SphereGeometry(100, 50, 50);
    var sphereMaterial = new THREE.MeshPhongMaterial({ color: 0xff0000, transparent: true, opacity: 1 });
    sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
    sphere1 = new THREE.Mesh(sphereGeometry, sphereMaterial);
    sphere.position.set(0, 30, 0);
    sphere1.position.set(-40, 100, 0);
    //scene.add(sphere);
    //scene.add(sphere1);
    //objects.push(sphere);
    //objects.push(sphere1);

    // adding the axis helper and the gui which is used to manipulate the 3d object
    var axes = new THREE.AxisHelper(10);
    scene.add(axes);
    gui = new dat.GUI();
    parameters =
    {
        x: 0, y: 30, z: 0,
        color: "#ff0000", // color (change "#" to "0x")
        colorA: "#000000", // color (change "#" to "0x")
        colorE: "#000033", // color (change "#" to "0x")
        colorS: "#ffff00", // color (change "#" to "0x")
        shininess: 30,
        opacity: 1,
        visible: true,
        material: "Phong",
        reset: function () { resetSphere() }
    };

    // wiring up the parameters of the shape that would be selected
    var folder1 = gui.addFolder('Position');
    var sphereX = folder1.add(parameters, 'x').min(-200).max(200).step(1).listen();
    var sphereY = folder1.add(parameters, 'y').min(-150).max(100).step(1).listen();
    var sphereZ = folder1.add(parameters, 'z').min(-200).max(200).step(1).listen();
    folder1.open();

    // listening to if there is a change to either one of the values for the position
    sphereX.onChange(function (value) {
        if (checkNull(currentObject) === false) {
            currentObject.selected.position.x = value;
        }
    });

    sphereY.onChange(function (value) {
        if (checkNull(currentObject) === false) {
            currentObject.selected.position.y = value;
        }
    });

    sphereZ.onChange(function (value) {
        if (checkNull(currentObject) === false) {
            currentObject.selected.position.z = value;
        }
    });

    var folder2 = gui.addFolder('Textures');

    // var sphereColor = gui.addColor(parameters, 'color').name('Color').listen();
    //sphereColor.onFinishChange(function (value) // onFinishChange
    //{
    //    // setMaterial(currentObject.selectedMesh, value.replace("#", "0x"));
    //    setMaterial(currentObject.selectedMesh, new THREE.MeshBasicMaterial({ color: 0xff0000 }));
    //});

    // wiring up the color change events
    //var sphereColor = gui.addColor(parameters, 'color').name('Color (Diffuse)').listen();
    //sphereColor.onChange(function (value) // onFinishChange
    //{ sphere.material.color.setHex(value.replace("#", "0x")); });
    var sphereColorA = folder2.addColor(parameters, 'colorA').name('Color (Ambient)').listen();
    sphereColorA.onChange(function (value) // onFinishChange
    {
        colorChangeClick = true;
        if (checkNull(currentObject) === false) {
            var color1 = value.replace("#", "0x");;
            //
            console.log(color1);
            var material = new THREE.MeshBasicMaterial({ color: color1 });
            // setMaterial(currentObject.selectedMesh, color);
            //setMaterial(currentObject.selectedMesh, material);
            currentObject.selectedMesh.material.color.setHex(color1);
        }
        colorChangeClick = false;
    });
    folder2.open();
    //    var sphereColorE = gui.addColor(parameters, 'colorE').name('Color (Emissive)').listen();
    //sphereColorE.onChange(function (value) // onFinishChange
    //{ sphere.material.emissive.setHex(value.replace("#", "0x")); });
    //var sphereColorS = gui.addColor(parameters, 'colorS').name('Color (Specular)').listen();
    //sphereColorS.onChange(function (value) // onFinishChange
    //{ sphere.material.specular.setHex(value.replace("#", "0x")); });
    //var sphereShininess = gui.add(parameters, 'shininess').min(0).max(60).step(1).name('Shininess').listen();
    //sphereShininess.onChange(function (value)
    //{ sphere.material.shininess = value; });
    //var sphereOpacity = gui.add(parameters, 'opacity').min(0).max(1).step(0.01).name('Opacity').listen();
    //sphereOpacity.onChange(function (value)
    //{ sphere.material.opacity = value; });
    //var sphereMaterial = gui.add(parameters, 'material', ["Basic", "Lambert", "Phong", "Wireframe"]).name('Material Type').listen();
    //sphereMaterial.onChange(function (value)
    //{ updateSphere(); });
    //gui.add(parameters, 'reset').name("Reset Sphere Parameters");
    gui.open();
    updateSphere();
}
function updateSphere() {
    var value = parameters.material;
    var newMaterial;
    if (value == "Basic")
        newMaterial = new THREE.MeshBasicMaterial({ color: 0x000000 });
    else if (value == "Lambert")
        newMaterial = new THREE.MeshLambertMaterial({ color: 0x000000 });
    else if (value == "Phong")
        newMaterial = new THREE.MeshPhongMaterial({ color: 0x000000 });
    else // (value == "Wireframe")
        newMaterial = new THREE.MeshBasicMaterial({ wireframe: true });
    sphere.material = newMaterial;
    sphere.position.x = parameters.x;
    sphere.position.y = parameters.y;
    sphere.position.z = parameters.z;
    sphere.material.color.setHex(parameters.color.replace("#", "0x"));
    if (sphere.material.ambient)
        sphere.material.ambient.setHex(parameters.colorA.replace("#", "0x"));
    if (sphere.material.emissive)
        sphere.material.emissive.setHex(parameters.colorE.replace("#", "0x"));
    if (sphere.material.specular)
        sphere.material.specular.setHex(parameters.colorS.replace("#", "0x"));
    if (sphere.material.shininess)
        sphere.material.shininess = parameters.shininess;
    sphere.material.opacity = parameters.opacity;
    sphere.material.transparent = true;
}

function resetSphere() {
    parameters.x = 0;
    parameters.y = 30;
    parameters.z = 0;
    parameters.color = "#ff0000";
    parameters.colorA = "#000000";
    parameters.colorE = "#000033";
    parameters.colorS = "#ffff00";
    parameters.shininess = 30;
    parameters.opacity = 1;
    parameters.visible = true;
    parameters.material = "Phong";
    updateSphere();
}

function animate() {
    requestAnimationFrame(animate);
    render();

    update();
}

function update() {
    controls.update();
    stats.update();
}

function render() {
    renderer.render(scene, camera);
}

// adding a mouse down event listener
document.addEventListener("click", function (event) {
    clickInfo.x = event.clientX;
    clickInfo.y = event.clientY;

    if (clickInfo.x >= 1760 && clickInfo.y >= 195) {
    }
    else {
        var x = (clickInfo.x / SCREEN_WIDTH) * 2 - 1;
        var y = -(clickInfo.y / SCREEN_HEIGHT) * 2 + 1;

        // Now we set our direction vector to those initial values
        directionVector.set(x, y, 1);

        // Unproject the vector
        projector.unprojectVector(directionVector, camera);

        // Substract the vector representing the camera position
        directionVector.sub(camera.position);

        // Normalize the vector, to avoid large numbers from the
        // projection and substraction
        directionVector.normalize();

        // Now our direction vector holds the right numbers!
        var raycaster = new THREE.Raycaster(camera.position, directionVector);

        // Instead of passing the whole scene, the set of objects are
        // added to an array and the array of objects are passed to the
        // intersectObjects method of the Raycaster class
        var intersects = raycaster.intersectObjects(objects, true);

        if (intersects.length > 0) {
            // intersections are, by default, ordered by distance,
            // so we only care for the first one. The intersection
            // object holds the intersection point, the face that's
            // been "hit" by the ray, and the object to which that
            // face belongs. We only care for the object itself.
            var target = intersects[0].object;
            alert(target.id);
            currentObject.selectedMesh = target;
            currentObject.selected = findSelectedObject(target, objects);
        }
        else {
            //if (colorChangeClick === false) {
            currentObject.selectedMesh = null;
            currentObject.selected = null;
            //}

        }
    }


});

// helper methods to find the selected mesh
// First pass the initial mesh that was selected
// along with the object array for comparision
function findSelectedObject(selectedMesh, objectArray) {
    var currentObject;

    for (var i = 0; i < objectArray.length; i++) {
        currentObject = objectArray[i];
        console.log(currentObject);
        if (doesMeshMatch(currentObject, selectedMesh) == true) {
            break;
        }

        else if (findInChildren(currentObject, selectedMesh) == true) {
            break;
        }
    }

    return currentObject;
}

function findInChildren(currentObject, selectedMesh) {
    var found = false;
    for (var j = 0; j < currentObject.children.length; j++) {
        if (doesMeshMatch(currentObject.children[j], selectedMesh) == true) {
            found = true;
            return true;
        }
        if (found == false) {
            if (currentObject.children[j].children.length > 0) {
                return findInChildren(currentObject.children[j], selectedMesh);
            }
            else {
                return false;
            }
        }
    }
}

// Check if the mesh matches based on a single uuid
function doesMeshMatch(currentObject, selectedObject) {
    if (currentObject.uuid === selectedObject.uuid) {
        console.log('Success');
        return true;
    }
    else {
        return false;
    }
}

// OBJMT LOADER
// code that loads a custom 3d object in .obj format
//function loadOBJMT(x, y) {
//    var loader = new THREE.OBJMTLLoader();
//    loader.addEventListener('load', function (event) {
//        var core = event.content;
//        core.position.x = x;//object.xPos;
//        core.position.y = y;//object.yPos;
//        scene.add(core);
//        objects.push(core);
//    });
//    loader.load('object/male/male02.obj', 'object/male/male02_dds.mtl');
//}

function loadOBJMT(obj) {
    var loader = new THREE.OBJMTLLoader();
    loader.addEventListener('load', function (event) {
        obj.core = event.content;
        obj.core.position.x = obj.xPos;
        obj.core.position.y = obj.yPos;
        scene.add(obj.core);
        objects.push(obj.core);
    });
    loader.load(obj.threedObj.obj, obj.threedObj.mtl);
    //loader.addEventListener('load', function (event) {
    //    var object = event.content;
    //    object.position.x = -200;
    //    object.position.y = -80;

    //    scene.add(object);
    //    objects.push(object);
    //});
    //loader.load('object/male/male02.obj', 'object/male/male02_dds.mtl');
}

function loadCollada(model) {
    var loader = new THREE.ColladaLoader();
    loader.options.convertUpAxis = true;

    //loader.addEventListener('load', function (event) {
    //    obj.core = event.content;
    //    obj.core.position.x = obj.xPos;
    //    obj.core.position.y = obj.yPos;
    //    scene.add(obj.core);
    //    objects.push(obj.core);
    //});
    //loader.load(obj.threedObj.obj, obj.threedObj.mtl);

    loader.load(model, function (collada) {
        dae = collada.scene;
        skin = collada.skins[0];
        dae.position.x = -120;
        dae.position.y = -80;
        dae.scale.x = dae.scale.y = dae.scale.z = 2.5;
        //dae.scale.x = dae.scale.y = dae.scale.z = 0.002;
        dae.updateMatrix();

        scene.add(dae);
        objects.push(dae);
        //init();
        //animate();
    });
}

function checkVar(returnObject) {
    if (returnObject != null) {
        return;
    }
    else {
        window.setTimeout("checkVar(" + returnObject + ");", 10);
    }
}

function doKeyDown(e) {
    if (keyboard.pressed("z")) {
        currentObject.scale.x += .01;
        currentObject.scale.y += .01;
    }
    if (keyboard.pressed("x")) {
        currentObject.scale.x -= .01;
        currentObject.scale.y -= .01;
    }
    if (keyboard.pressed("c")) {
        setMaterial(currentObject.selectedMesh, new THREE.MeshBasicMaterial({ color: 0xff0000 }));
        // loadFloorTexture('checkerboard');
    }
    if (keyboard.pressed("d")) {
        //loadFloorTexture('wood');
        debugger;
        setMaterial(currentObject.selectedMesh, new THREE.MeshBasicMaterial({ color: 0x95d2ca }));
    }
}

function loadFloorTexture(location) {
    var floorTexture = new THREE.ImageUtils.loadTexture('images/' + location + '.jpg');
    floor.material.map = floorTexture;
}

function checkNull(object) {
    var mesh = check(object.selectedMesh);
    var obj = check(object.selectedObject);

    if (mesh === true && obj === true) {
        return true;
    }
    else {
        return false;
    }

}

function check(comp) {
    if (comp === undefined) {
        return true;
    }
    else if (comp === null) {
        return true;
    }
    else {
        return false;
    }
}

// set the material to a part of the model and
// its children
function setMaterial(selectedNode, material) {
    selectedNode.material = material;
    if (selectedNode.children.length > 0) {
        for (var i = 0; i < selectedNode.children.length; i++) {
            //
            setMaterial(selectedNode.children[i], material);
        }
    }
}